package com.yuyu.commons.es;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.search.TotalHits;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.core.CountRequest;
import org.elasticsearch.client.core.CountResponse;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.client.indices.PutMappingRequest;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.reindex.BulkByScrollResponse;
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

@Service
@Slf4j
public class EsUtils {

    @Resource
    private EsHighClient esClient;

    /**
     * 创建索引
     * @param index
     * @return
     * @throws IOException
     */
    public boolean creatIndex(String index) throws IOException {
     if (isIndexExist(index)){
      log.error("index is exist!!");
      return false;
     }
        //创建索引请求
      CreateIndexRequest createIndexRequest = new CreateIndexRequest(index);
      //施行创建索引
      CreateIndexResponse createIndexResponse = esClient.indices().create(createIndexRequest, RequestOptions.DEFAULT);
      return createIndexResponse.isAcknowledged();
    }


    /**
     * 条件删除某个数据
     * @param deleteByQueryRequest
     * @throws IOException
     */
    public void deleteSingleByCondition(DeleteByQueryRequest deleteByQueryRequest) throws IOException {
        if (Objects.isNull(deleteByQueryRequest)){
            log.error("param is null!!");
        }
        BulkByScrollResponse response = esClient.deleteByQuery(deleteByQueryRequest, RequestOptions.DEFAULT);
    }

    /**
     * 判断索引是否存在
     * @param index
     * @return
     * @throws IOException
     */
    public boolean isIndexExist(String index) throws IOException {
        GetIndexRequest request=new GetIndexRequest(index);
        return esClient.indices().exists(request, RequestOptions.DEFAULT);
    }

    /**
     * 删除索引
     * @param index
     * @return
     */
    public boolean deleteIndex(String index) throws IOException {
        if (!isIndexExist(index)){
            log.error("index is not exist!!");
            return false;
        }
        DeleteIndexRequest deleteIndexRequest=new DeleteIndexRequest(index);
        AcknowledgedResponse delete = esClient.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT);
        return delete.isAcknowledged();
    }

    /**
     *修改字段类型
     * @param index
     * @param mapping
     * @return
     */
    public boolean updateFieldType(String index, XContentBuilder mapping) throws IOException {
        PutMappingRequest putMappingRequest=new PutMappingRequest(index);
        putMappingRequest.source(mapping);
        AcknowledgedResponse acknowledgedResponse = esClient.indices().putMapping(putMappingRequest, RequestOptions.DEFAULT);
        return acknowledgedResponse.isAcknowledged();
    }

    /**
     * 向指定得索引添加数据
     * @param index
     * @param object
     * @return
     */
    public Boolean insertDoc(String index ,Object object) {
        try {
            //插入数据 必须将数据转换为json格式
            IndexRequest request = new IndexRequest();
            request.index(index).id(String.valueOf(getFiledValueByName("id",object)));
            String jsonObject = JSONObject.toJSONString(object);
            //将json格式化得对象放入source
            request.source(jsonObject, XContentType.JSON);
            //执行请求
            IndexResponse response = esClient.index(request, RequestOptions.DEFAULT);
            log.info("插入得数据详情为:{}", response.getResult());
            return true;
        }catch (Exception e){
            e.printStackTrace();
            log.info("插入失败原因为{}",e.getMessage());
            return false;
        }

    }

    /**
     * 批量插入（注意经纬度必须为真实的而且要为gps84,不然存不进去)
     * @param index
     * @param objects
     * @return
     */
    public<T> boolean bulkPost(String index, List<T> objects){
        BulkRequest bulkRequest=new BulkRequest();
        //最大数量不超过20万
        for (Object object:objects) {
         IndexRequest indexRequest=new IndexRequest(index);
         indexRequest.source(JSONObject.toJSONString(object),XContentType.JSON);
         //通过泛型来获取id
         String id=getFiledValueByName("id",object);
         if (StringUtils.isNotEmpty(id)){
             indexRequest.id(id);
         }
         bulkRequest.add(indexRequest);
        }
        try {
          log.info("bulkRequest为》》》》》》》》》》》》》》》》》》》》》》》》》》》》{}",bulkRequest.getDescription());
          esClient.bulk(bulkRequest,RequestOptions.DEFAULT);
        }catch (Exception e){
        }
        return Boolean.TRUE;
    }

    private String getFiledValueByName(String filed, Object object) {
      try {
          //取出id中的i然后大写
          String firstFiledName=filed.substring(0,1).toUpperCase();
          //取出id中的d
          String endFiledName=filed.substring(1);
          String methodName="get"+firstFiledName+endFiledName;
          //得到object的泛型
          Class<?> aClass = object.getClass();
          Method method = aClass.getMethod(methodName);
          //唤醒方法 获取getId的值
          return (String) method.invoke(object);
      }catch (Exception e){
          e.printStackTrace();
          log.info("获取字段值失败!!");
      }
      return null;
    }

    /**
     * 修改指定索引的数据(局部更新)
     * @param index
     * @param id
     * @param o
     * @return
     */
    public Boolean updateDoc(String index,String id,Object o){
        try {
            UpdateRequest updateRequest=new UpdateRequest();
            updateRequest.index(index).id(id);
            updateRequest.setRefreshPolicy("wait_for");
            updateRequest.timeout("1s");
            //设置json格式
            updateRequest.doc(JSONObject.toJSONString(o),XContentType.JSON);
            UpdateResponse updateResponse = esClient.update(updateRequest, RequestOptions.DEFAULT);
            log.info("修改的数据详情为:{}",updateResponse.getResult());
            return true;
        }catch (Exception e){
            e.printStackTrace();
            log.info("修改失败的原因为{}",e.getMessage());
            return false;
        }
    }

    /**
     * get出来的map转指定对象
     * @param index
     * @param id
     * @param filed
     * @param tClass
     * @param <T>
     * @return
     * @throws IOException
     */
   public <T> T convertSearch(String index,String id,String filed,Class<T> tClass) throws IOException {
       T t = BeanUtil.mapToBean(searchDataById(index, id, filed), tClass, true,CopyOptions.create());
       return t;
   }
    /**
     * 通过索引以及id来查询
     * @param index 索引，类似数据库
     * @param id    数据ID
     * @param filed 需要显示的字段，逗号分隔（缺省为全部字段）
     * @return
     */
  public Map<String,Object> searchDataById(String index,String id,String filed) throws IOException {
      //创建查询request
     GetRequest getRequest = new GetRequest(index,id);
     if (StringUtils.isNotEmpty(filed)){
         //判断是否查询全部字段 如果设置显示字段就添加fetchSourceContext
         getRequest.fetchSourceContext(new FetchSourceContext(true,filed.split(","), Strings.EMPTY_ARRAY));
     }
     GetResponse response = esClient.get(getRequest, RequestOptions.DEFAULT);
     return response.getSource();
  }

    /**
     * 查询总数
     * @param index
     * @return
     */
  public Long count(String index){
      //指定创建时间
      BoolQueryBuilder queryBuilder=new BoolQueryBuilder();
      queryBuilder.must(QueryBuilders.termQuery("creatTime", System.currentTimeMillis()));

      SearchSourceBuilder searchSourceBuilder=new SearchSourceBuilder();
      searchSourceBuilder.query(queryBuilder);

      CountRequest countRequest=new CountRequest(index);
      countRequest.source(searchSourceBuilder);
     try {
         CountResponse count = esClient.count(countRequest, RequestOptions.DEFAULT);
         return count.getCount();
     }catch (Exception e){
         log.info("查询失败的原因为:{}",e.getMessage());
     }
     return 0L;
  }
    /**
     *
     * @param page 分页
     * @param tClass 返回类型
     * @param <T> 泛型
     * @return
     */
  public <T> IPage<T>   searchListDate(Page<T> page,Class<T> tClass,String indexName,SearchSourceBuilder searchSourceBuilder){
     //搜索全部
      SearchRequest searchRequest=new SearchRequest(indexName);
      searchRequest.searchType(SearchType.DFS_QUERY_THEN_FETCH);
      //开始页
      searchSourceBuilder.from(Long.valueOf(page.getCurrent()).intValue()-1);
      //每页大小
      searchSourceBuilder.size(Long.valueOf(page.getSize()).intValue());
      searchRequest.source(searchSourceBuilder);
      try {
          SearchResponse searchResponse = esClient.search(searchRequest, RequestOptions.DEFAULT);
          List<String> data=new ArrayList<>();
          if (searchResponse.status().getStatus()==200){
              SearchHit[] hits = searchResponse.getHits().getHits();
              for (SearchHit searchHit:hits) {
                  //命中的Json转对象
                  String sourceAsString = searchHit.getSourceAsString();
                  data.add(sourceAsString);
              }
              TotalHits totalHits = searchResponse.getHits().getTotalHits();
              page.setTotal(totalHits.value);
              log.info("data的数据为》》》》》》》》》》》》》》》》》》》》》》》》》》》{}",data);
              page.setRecords(stringToObject(data,tClass));
              return page;
          }
      }catch (Exception e){
         log.info("查询失败的原因为>>>>>>>>>>>>>>>>>>>>>>>>>>>>:{}",e.getMessage());
      }
      return null;
  }

    /**
     * @param strings
     * @param beanClass
     * @return
     */
   private <T> List<T> stringToObject(List<String> strings,Class<T> beanClass) {
       List<T> list= new ArrayList<>();
     if (ObjectUtil.isEmpty(strings)){
           return null;
     }
       for (String s:strings) {
           T t = JSON.parseObject(s, beanClass);
           list.add(t);
       }
        return list;
   }

}